package org.simpleframework.core;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.simpleframework.aop.annotation.Aspect;
import org.simpleframework.core.annotation.Component;
import org.simpleframework.core.annotation.Controller;
import org.simpleframework.core.annotation.Repository;
import org.simpleframework.core.annotation.Service;
import org.simpleframework.util.ClassUtil;
import org.simpleframework.util.ValidationUtil;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 〈〉
 *
 * @author Noah2021
 * @create 2021/3/7
 * @return
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
    //存放所有被配置标记的目标对象的Map
    private final Map<Class<?>, Object> beanMap = new ConcurrentHashMap();
    //加载bean的注解列表
    private final List<Class<? extends Annotation>> BEAN_ANNOTATION =
            Arrays.asList(Controller.class, Service.class, Component.class, Repository.class, Aspect.class);

    private enum ContainerHolder {
        HOLDER;
        private BeanContainer instance;

        ContainerHolder() {
            instance = new BeanContainer();
        }
    }
    //获取bean容器实例
    public static BeanContainer getInstance() {
        return ContainerHolder.HOLDER.instance;
    }

    //是否加载过bean
    private boolean loaded = false;
    public boolean isLoaded() {
        return loaded;
    }
    //bean实例数量
    public int size() {
        return beanMap.size();
    }
    //扫描加载所有的bean
    public synchronized void loadBeans(String packageName) {
        //判断bean容器是否被加载过
        if(isLoaded()){
            System.out.println("【WARN】BeanContainer has been loaded");
            return;
        }
        Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
        //新建一个验证工具类ValidationUtil用于字符串、数组等的判空校验
        if(ValidationUtil.isEmpty(classSet)){
            System.out.println("【WARN】extract nothing from packageName" + packageName);
            return;
        }
        for (Class clazz:classSet) {
            for (Class<? extends Annotation> annotation: BEAN_ANNOTATION) {
                //如果类上面标记了定义的注解
                if(clazz.isAnnotationPresent(annotation))
                    //将目标类本身作为键，目标类的实例作为值，放入到beanMap中,定义一个newInstance方法用于通过反射初始化对象
                    beanMap.put(clazz, ClassUtil.newInstance(clazz,true));
            }
        }
        loaded = true;
    }

    //容器的增删改查
    /**
     * 添加一个class对象及其Bean实例
     * @param clazz Class对象
     * @param bean  Bean实例
     * @return 原有的Bean实例, 没有则返回null
     */
    public Object addBean(Class<?> clazz, Object bean) {
        return beanMap.put(clazz, bean);
    }
    /*移除一个IOC容器管理的对象*/
    public Object removeBean(Class<?> clazz) {
        return beanMap.remove(clazz);
    }
    /*根据Class对象获取Bean实例*/
    public Object getBean(Class<?> clazz) {
        return beanMap.get(clazz);
    }
    /*获取容器管理的所有Class对象集合*/
    public Set<Class<?>> getClasses(){
        return beanMap.keySet();
    }
    /*获取所有Bean集合*/
    public Set<Object> getBeans(){
        return new HashSet<>( beanMap.values());
    }
    /*根据注解筛选出Bean的Class集合*/
    public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation){
        //1.获取beanMap的所有class对象
        Set<Class<?>> keySet = getClasses();
        if(ValidationUtil.isEmpty(keySet)){
            //log.warn("nothing in beanMap");
            System.out.println("【WARN】nothing in beanMap");
            return null;
        }
        //2.通过注解筛选被注解标记的class对象，并添加到classSet里
        Set<Class<?>> classSet = new HashSet<>();
        for(Class<?> clazz : keySet){
            //类是否有相关的注解标记
            if(clazz.isAnnotationPresent(annotation)){
                classSet.add(clazz);
            }
        }
        return classSet.size() > 0? classSet: null;
    }
    /*通过接口或者父类获取实现类或者子类的Class集合，不包括其本身*/
    public Set<Class<?>> getClassesBySuper(Class<?> interfaceOrClass){
        //1.获取beanMap的所有class对象
        Set<Class<?>> keySet = getClasses();
        if(ValidationUtil.isEmpty(keySet)){
            //log.warn("nothing in beanMap");
            System.out.println("【WARN】nothing in beanMap");
            return null;
        }
        //2.判断keySet里的元素是否是传入的接口或者类的子类，如果是，就将其添加到classSet里
        Set<Class<?>> classSet = new HashSet<>();
        for(Class<?> clazz : keySet){
            //判断keySet里的元素是否是传入的接口或者类的子类
            if(interfaceOrClass.isAssignableFrom(clazz) && !clazz.equals(interfaceOrClass)){
                classSet.add(clazz);
            }
        }
        return classSet.size() > 0? classSet: null;
    }
}